package org.dromara.common.web.filter;

import org.dromara.common.core.utils.StringUtils;
import org.springframework.http.MediaType;

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;


/**
 * @Author 刘武贵
 * @Date 2024/8/15 22:00
 * @Description 功能描述：Repeatable 过滤器
 */
/**
 * 实现Filter接口的可重复使用过滤器类
 * 该类旨在处理过滤逻辑，以便在不同的场景中重复使用相同的过滤逻辑
 */
public class RepeatableFilter implements Filter {

    /**
     * 初始化过滤器
     * <p>
     * 该方法用于在过滤器实例化后初始化过滤器，配置相关信息
     *
     * @param filterConfig 过滤器配置对象，可以通过它获取过滤器的配置信息
     * @throws ServletException 如果初始化过程中发生异常，抛出该异常
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    /**
     * 重写过滤器的doFilter方法，以处理JSON类型的请求体
     * 该方法检查ServletRequest是否为HttpServletRequest的实例，并且其内容类型为JSON
     * 如果满足条件，则使用自定义的RepeatedlyRequestWrapper包装请求
     * 这允许在过滤器链中保持请求的原始数据不变
     *
     * @param request  进入过滤器链的请求对象
     * @param response 从过滤器链返回的响应对象
     * @param chain    用于调用下一个过滤器或最终的资源的过滤器链
     * @throws IOException      如果在处理请求时发生输入/输出异常
     * @throws ServletException 如果在过滤器链中发生异常
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        // 初始化请求包装器为null
        ServletRequest requestWrapper = null;
        // 检查请求是否为HTTP请求且内容类型为JSON
        if (request instanceof HttpServletRequest
            && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) {
            // 如果是，创建一个包装请求和响应的自定义请求包装器实例
            requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response);
        }
        // 如果没有创建请求包装器，直接使用原始请求继续过滤器链
        if (null == requestWrapper) {
            chain.doFilter(request, response);
        } else {
            // 如果创建了请求包装器，使用包装器继续过滤器链，以保持请求数据的可重复读取
            chain.doFilter(requestWrapper, response);
        }
    }

    /**
     * 销毁当前实例的方法
     *
     * 此方法被重写自父类或接口，其主要作用是在不再需要使用当前实例时，
     * 调用它来释放该实例所占用的资源池。在默认情况下，该方法没有具体实现，
     * 但可以在子类中被覆盖以执行特定的销毁逻辑。
     */
    @Override
    public void destroy() {

    }
}
